package com.hzeng.util;

import java.awt.Color;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.ui.RectangleInsets;

import au.com.bytecode.opencsv.CSVReader;

public class MyTimeSeriesChartUtil
{
	
	private static MyTimeSeriesChartUtil myTimeSeriesChart = new MyTimeSeriesChartUtil();
	
	
	private MyTimeSeriesChartUtil() {
		
	}
	
	public static MyTimeSeriesChartUtil getInstance() {
		if (myTimeSeriesChart == null) {
			myTimeSeriesChart = new MyTimeSeriesChartUtil();
		}
		return myTimeSeriesChart;
	}
	
	
	
	Map<Integer, JFreeChart> charts = new HashMap<Integer, JFreeChart>();
	Map<Integer, TimeSeries> timeSeries = new HashMap<Integer, TimeSeries>();
	
	private File saveFile = null;
	
	JFreeChart chart = null;
	TimeSeries tempTimeSeries = null;
	TimeSeriesCollection dataset = null;
	private Program programSchema = null;
	Long pid = null;
	
	

	public Map<Integer, JFreeChart> getCharts() {
		return charts;
	}

	public void setCharts(Map<Integer, JFreeChart> charts) {
		this.charts = charts;
	}

	public TimeSeries getTempTimeSeries() {
		return tempTimeSeries;
	}

	public void setTempTimeSeries(TimeSeries tempTimeSeries) {
		this.tempTimeSeries = tempTimeSeries;
	}

	public Program getProgramSchema() {
		return programSchema;
	}

	public void setProgramSchema(Program programSchema) {
		this.programSchema = programSchema;
	}



	private List<Program> programs = null;

	public File getSaveFile() {
		return saveFile;
	}

	public void setSaveFile(File saveFile) {
		this.saveFile = saveFile;
	}

	public List<Program> getPrograms() {
		return programs;
	}

	public void setPrograms(List<Program> programs) {
		this.programs = programs;
	}

	public Long getPid() {
		return pid;
	}

	public void setPid(Long pid) {
		this.pid = pid;
	}
	
	public long loadProgramRange() {
		long dateBegin = 0l;
		long dateEnd = 0l;
		if (saveFile == null || (saveFile.exists() == false)) {
			return 0l;
		}
		CSVReader reader = CSVUtil.readFile(saveFile);
		
		String[] schema = null;
		long rowNum = 0l;
		while ((schema= CSVUtil.readRowData(reader)) != null) {
			if (rowNum == 0) {
				dateBegin = new Long(schema[0]);
			}
			dateEnd = new Long(schema[0]);
			rowNum++;
		}
		
		return dateEnd - dateBegin;
	}
	
	public Program loadProgramSchema() {
		if (saveFile == null || (saveFile.exists() == false)) {
			return null;
		}
		Program program = new Program();
		String[] schema = CSVUtil.readSchema(saveFile);
		
		for (int i = 0; i < schema.length; i++) {
			String tempString = schema[i];
			String[] temStrings = tempString.split(": ");
			if (tempString.startsWith(Constants._SCHEMA_PREFIX)) {
				if (temStrings[0].endsWith(Constants._PID)) {
					program.setPid(new Long(temStrings[1]));
				}
			}else {
				if (temStrings[0].equals(Constants._NAME)) {
					program.setName(temStrings[1]);
				}else if (temStrings[0].equals(Constants._BIN)) {
					program.setBin(temStrings[1]);
				}else if (temStrings[0].equals(Constants._CWD)) {
					program.setDirectoryName(temStrings[1]);
				}
			}
		}
		
		return program;
	}
	
	public Map<Integer, JFreeChart> createStaticCharts() {
		programSchema = loadProgramSchema();
		long dateRange = loadProgramRange();
		
		charts.put(Constants.CHART_CPU, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._CPU, programSchema.getName(),dateRange));
		charts.put(Constants.CHART_MEMORY, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._MEMORY, programSchema.getName(),dateRange));
		if (programSchema.getName().equals(Constants._SYS_NAME) && programSchema.getPid() == 0) {
			charts.put(Constants.CHART_TOTAL_MEMORY, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._TOTAL_MEMORY, programSchema.getName(),dateRange));
			charts.put(Constants.CHART_SYS_READ, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._SYS_READ, programSchema.getName(),dateRange));
			charts.put(Constants.CHART_SYS_WRITE, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._SYS_WRITE, programSchema.getName(),dateRange));
			
		}else {
			charts.put(Constants.CHART_VM, createStaticChart(programSchema.getName() + "/" + programSchema.getPid(), Constants._TIME, Constants._VM, programSchema.getName(),dateRange));
		}
		
		return charts;
	}

	public JFreeChart createStaticChart(String title,String xLabel,String yLabel,String seriesTitle,long dateRange) {
		 	tempTimeSeries = new TimeSeries(seriesTitle);
		 	if (yLabel.equals(Constants._CPU)) {
		 		timeSeries.put(Constants.CHART_CPU, tempTimeSeries);
		 		yLabel = yLabel + " (%)";
			}else if (yLabel.equals(Constants._MEMORY)) {
				timeSeries.put(Constants.CHART_MEMORY, tempTimeSeries);
				yLabel = yLabel + " (M)";
			}else if (yLabel.equals(Constants._VM)) {
				timeSeries.put(Constants.CHART_VM, tempTimeSeries);
				yLabel = yLabel + " (M)";
			}else if (yLabel.equals(Constants._TOTAL_MEMORY)) {
				timeSeries.put(Constants.CHART_TOTAL_MEMORY, tempTimeSeries);
				yLabel = yLabel + " (M)";
			}else if (yLabel.equals(Constants._READ)) {
				timeSeries.put(Constants.CHART_READ, tempTimeSeries);
				yLabel = yLabel + " (K/S)";
			}else if (yLabel.equals(Constants._WRITE)) {
				timeSeries.put(Constants.CHART_WRITE, tempTimeSeries);
				yLabel = yLabel + " (K/S)";
			}else if (yLabel.equals(Constants._SYS_READ)) {
				timeSeries.put(Constants.CHART_SYS_READ, tempTimeSeries);
				yLabel = yLabel + " (K/S)";
			}else if (yLabel.equals(Constants._SYS_WRITE)) {
				timeSeries.put(Constants.CHART_SYS_WRITE, tempTimeSeries);
				yLabel = yLabel + " (K/S)";
			}
		 	
		 	
			dataset = new TimeSeriesCollection();
			dataset.addSeries(tempTimeSeries);
			tempTimeSeries = null;

	        chart = ChartFactory.createTimeSeriesChart(
	        	title,  // title
	        	xLabel,             // x-axis label
	        	yLabel,   // y-axis label
	            dataset,            // data
	            true,               // create legend?
	            true,               // generate tooltips?
	            false               // generate URLs?
	        );

	        chart.setBackgroundPaint(Color.white);

	        XYPlot plot = (XYPlot) chart.getPlot();
	        
	        DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
	        
	        
	        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
	        /*
	        TickUnits tickUnits = new TickUnits();
	        tickUnits.add(new DateTickUnit(DateTickUnitType.YEAR, 1, mouthFormat));
	        tickUnits.add(new DateTickUnit(DateTickUnitType.MONTH, 1, dayFormat));
	        tickUnits.add(new DateTickUnit(DateTickUnitType.DAY, 7, HourFormat));
	        tickUnits.add(new DateTickUnit(DateTickUnitType.HOUR, 12, secondFormat));
	        
	        domainAxis.setStandardTickUnits(tickUnits);
	        */
	        
	        domainAxis.setAutoRange(true);
	        
	        //set date range(max date - min date) to 12 * 60 * 60 * 1000 millisecond
	        if (dateRange < Constants.ONE_MINUTE) {
	        	dateRange = Constants.ONE_MINUTE;
			}else if (dateRange < Constants.FIVE_MINUTE) {
				dateRange = dateRange + Constants.ONE_MINUTE - (dateRange % Constants.ONE_MINUTE);
			}else if (dateRange < Constants.ONE_HOUR) {
				dateRange = dateRange + Constants.FIVE_MINUTE - (dateRange % Constants.FIVE_MINUTE);
			}else if (dateRange < Constants.TWELVE_HOUR) {
				dateRange = dateRange + Constants.ONE_HOUR - (dateRange % Constants.ONE_HOUR);
				dateFormat = new SimpleDateFormat("HH:mm:ss");
			}else if (dateRange < Constants.ONE_DAY) {
				dateRange = dateRange + Constants.TWO_HOUR - (dateRange % Constants.TWO_HOUR);
				dateFormat = new SimpleDateFormat("dd HH:mm");
			}else if (dateRange < Constants.ONE_WEEK) {
				dateRange = dateRange + Constants.TWELVE_HOUR - (dateRange % Constants.TWELVE_HOUR);
				dateFormat = new SimpleDateFormat("MM-dd HH");
			}else if (dateRange < Constants.ONE_MOUTH) {
				dateRange = dateRange + Constants.ONE_DAY - (dateRange % Constants.ONE_DAY);
				dateFormat = new SimpleDateFormat("MM-dd");
			}else if (dateRange < Constants.ONE_YEAR) {
				dateRange = dateRange + Constants.ONE_MOUTH - (dateRange % Constants.ONE_MOUTH);
				dateFormat = new SimpleDateFormat("yyyy-MM");
			}else {
				dateRange = dateRange + Constants.HALF_YEAR - (dateRange % Constants.HALF_YEAR);
				dateFormat = new SimpleDateFormat("yyyy");
			}
			
	        domainAxis.setFixedAutoRange(dateRange);
	        domainAxis.setDateFormatOverride(dateFormat);
	        
	        domainAxis.setTickLabelsVisible(true);
	        domainAxis.setTickMarkInsideLength(10);
	        domainAxis.setTickLabelInsets(new RectangleInsets(10.0, 5.0, 10.0,5.0));
	       // domainAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
	        
	        plot.setBackgroundPaint(Color.lightGray);
	        plot.setDomainGridlinePaint(Color.white);
	        plot.setRangeGridlinePaint(Color.white);
	        plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
	        plot.setDomainCrosshairVisible(true);
	        plot.setRangeCrosshairVisible(false);
	        
	        XYItemRenderer r = plot.getRenderer();
	        if (r instanceof XYLineAndShapeRenderer) {
	            XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
	            renderer.setBaseShapesVisible(false);
	            renderer.setBaseShapesFilled(true);
	        }
	        
	        
	        return chart;
	 }
	
    
    public void createStaticDataset() {
    	if (programs == null) {
			
		}else {
			TimeSeries cpuSeries = timeSeries.get(Constants.CHART_CPU);
	    	TimeSeries memorySeries = timeSeries.get(Constants.CHART_MEMORY);
	    	TimeSeries vmSeries = null;
	    	TimeSeries totalMemorySeries = null;
	    	TimeSeries readSeries = null;
	    	TimeSeries writeSeries = null;
	    	if (programSchema.getName().equals(Constants._SYS_NAME) && programSchema.getPid() == 0) {
		    	totalMemorySeries = timeSeries.get(Constants.CHART_TOTAL_MEMORY);
		    	readSeries = timeSeries.get(Constants.CHART_SYS_READ);
		    	writeSeries = timeSeries.get(Constants.CHART_SYS_WRITE);
			}else {
				vmSeries = timeSeries.get(Constants.CHART_VM);
			}
	    	
	    	if (vmSeries == null) {
	    		for (int i = 0; i < programs.size(); i++) {
					Program p = programs.get(i);
					
		    		cpuSeries.add(new Second(new Date(p.getTimestamp())), p.getCpu());
		    		memorySeries.add(new Second(new Date(p.getTimestamp())), p.getMemory());
		    		totalMemorySeries.add(new Second(new Date(p.getTimestamp())), p.getTotalMemory());
		    		readSeries.add(new Second(new Date(p.getTimestamp())), p.getRead());
			    	writeSeries.add(new Second(new Date(p.getTimestamp())), p.getWrite());
				}
			}else {
				for (int i = 0; i < programs.size(); i++) {
					Program p = programs.get(i);
					
		    		cpuSeries.add(new Second(new Date(p.getTimestamp())), p.getCpu());
		    		memorySeries.add(new Second(new Date(p.getTimestamp())), p.getMemory());
		    		vmSeries.add(new Second(new Date(p.getTimestamp())), p.getVirtualMemory());
				}
			}
		}
    }
    
    public List<File> buildImagebyFile() {
    	List<File> files = new ArrayList<File>();
    	
    	createStaticCharts();
		createStaticDataset();
		
		File cpuImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._CPU + Constants.TYPE_JPG);
		File memoryImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._MEMORY + Constants.TYPE_JPG);
		
		File totalMemoryImage = null;
		File vmImage = null;
		File readImage = null;
		File writeImage = null;
		if (programSchema.getName().equals(Constants._SYS_NAME) && programSchema.getPid() == 0) {
			totalMemoryImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._TOTAL_MEMORY + Constants.TYPE_JPG);
			createImage(Constants.CHART_TOTAL_MEMORY,totalMemoryImage);
			
			readImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._READ + Constants.TYPE_JPG);
			createImage(Constants.CHART_SYS_READ,readImage);			
			writeImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._WRITE + Constants.TYPE_JPG);
			createImage(Constants.CHART_SYS_WRITE,writeImage);
			
			files.add(totalMemoryImage);
			files.add(readImage);
			files.add(writeImage);
		}else {
			vmImage = new File(saveFile.getParentFile().getAbsolutePath() + "/" + programSchema.getPid() + "_" + Constants._VM + Constants.TYPE_JPG);
			createImage(Constants.CHART_VM,vmImage);
			files.add(vmImage);
		}
		createImage(Constants.CHART_CPU,cpuImage);
		createImage(Constants.CHART_MEMORY,memoryImage);
		files.add(cpuImage);
		files.add(memoryImage);
		return files;
    }
    
    public void createImage(int conpontent,File imagePath) {
    	for (int i = 0; i < charts.size(); i++) {
    		final JFreeChart chart = charts.get(conpontent);
    		
        	FileOutputStream fos_jpg = null; 
            try { 
                try {
    				fos_jpg = new FileOutputStream(imagePath);
    				ChartUtilities.writeChartAsJPEG(fos_jpg,1,chart,ProgramMonitorUI.imageWidth,ProgramMonitorUI.imageHeight,null);
    			} catch (FileNotFoundException e) {
    				e.printStackTrace();
    			}  catch (IOException e) {
    				e.printStackTrace();
    			}
                
            } finally { 
                try { 
                    fos_jpg.close(); 
                } catch (Exception e) {} 
            } 
		}
    }
    
}
